#ifndef _CRT_SECURE_NO_WARNINGS
	#define _CRT_SECURE_NO_WARNINGS // Deal with warnings in glui.h
#endif

#include <GL\glui.h>
#include <math.h>

#include "RGBpixmap.h"
#include "Drawer.h"
#include "../Textures.h"
#include "../skyboxes/SkyboxModelling.h"
#include "../boards/BoardModelling.h"
#include "../Views.h"

#define COORDX 0.5
#define COORDY 0.5
#define COORDZ 0.5

#define NQUAD_STRIPS 50
#define FLOOR_SLICES 200

GLfloat mat_floor_ambient[4] = {0.55, 0.55, 0.55, 1.0};
GLfloat mat_floor_diffuse[4] = {0.85, 0.85, 0.85, 1.0};
GLfloat mat_floor_specular[4] = {0.3, 0.3,0.3, 1.0};
GLfloat mat_floor_shininess[1] = {128};

// Floor evaluator declarations
GLfloat ctrlpoints[4][3] = {	{  0.0, 0.0, 0.0},
								{ 0.0, 0.0, 0.0},
								{  0.0, 0.0, 0.0},
								{ 0.0, 0.0, 0.0} };

GLfloat nrmlcompon[4][3] = {	{  0.0, 1.0, 0.0},
								{  0.0, 1.0, 0.0},    
								{  0.0, 1.0, 0.0},
								{  0.0, 1.0, 0.0} };

GLfloat colorpoints[4][4] = {	{ 0.0, 0.2, 0.2, 0},
								{ 0.0, 0.0, 0.2, 0}, 
								{ 0.0, 0.2, 0.0, 0},
								{ 0.2, 0.0, 0.0, 0} };

GLfloat textpoints[4][2] = {	{ 0.0, 0.0},
								{ 0.0, 1.0}, 
								{ 1.0, 0.0},
								{ 1.0, 1.0} };


int normals[3][3] = { {1,0,0}, {0,1,0}, {0,0,1} };

/** Deprecated =P */
void draw_surface(
        float v1x, float v1y, float v1z,
        float v2x, float v2y, float v2z,
        float v3x, float v3y, float v3z,
        float v4x, float v4y, float v4z,
        int tex_id, int normal)
{       
        if( tex_id >= 0 ) {
                glEnable(GL_TEXTURE_2D);
                glBindTexture(GL_TEXTURE_2D, tex_id);
        }
        else
                glEnable(GL_COLOR_MATERIAL);
        
        
        int normal_sign = (normal < 0 ? -1 : 1);
        normal *= (normal < 0 ? -1 : 1) - 1;

        glBegin(GL_QUADS);
        glNormal3i( normals[normal][0] * normal_sign, normals[normal][1] * normal_sign, normals[normal][2] * normal_sign);
                glTexCoord2d(1, 1); glVertex3f(v1x, v1y, v1z);
                glTexCoord2d(0, 1); glVertex3f(v2x, v2y, v2z);
                glTexCoord2d(0, 0); glVertex3f(v3x, v3y, v3z);
                glTexCoord2d(1, 0); glVertex3f(v4x, v4y, v4z);
        glEnd();

        if( tex_id >= 0)
                glDisable(GL_TEXTURE_2D);

}

/**
	Draws a unitary square in the XY plane, centered on the origin.
*/
void drawRect()
{
	glBegin(GL_POLYGON);
		glNormal3i(0,0,1);
		glVertex3f(COORDX, COORDY, 0.0);
		glVertex3f(-COORDX, COORDY, 0.0);
		glVertex3f(-COORDX, -COORDY, 0.0);
		glVertex3f(COORDX, -COORDY, 0.0);
	glEnd();
}

/**
	Draws a unitary square in the XY plane, centered on the origin.
*/
void drawRect(int tex_id)
{
	if( tex_id >= 0 ) {
		glEnable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D, tex_id);
	}
	else
		glEnable(GL_COLOR_MATERIAL);

	glBegin(GL_POLYGON);
		glNormal3i(0,0,1);
		glTexCoord2d(1, 1); glVertex3f(COORDX, COORDY, 0.0);
		glTexCoord2d(0, 1); glVertex3f(-COORDX, COORDY, 0.0);
		glTexCoord2d(0, 0); glVertex3f(-COORDX, -COORDY, 0.0);
		glTexCoord2d(1, 0); glVertex3f(COORDX, -COORDY, 0.0);
	glEnd();

	if( tex_id >= 0)
        glDisable(GL_TEXTURE_2D);
}

/**
	Draws a rectangle in the XY plane, centered on the origin, given the vertices.
*/
void drawRect(float x1, float x2, float y1, float y2, int tex_id)
{
    if( tex_id >= 0 ) {
        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, tex_id);
    }

	glBegin(GL_POLYGON);
		glNormal3i(0, 0, 1);
		glTexCoord2d(1, 1); glVertex3f(x2, y2, 0.0);
		glTexCoord2d(0, 1); glVertex3f(x1, y2, 0.0);
		glTexCoord2d(0, 0); glVertex3f(x1, y1, 0.0);
		glTexCoord2d(1, 0); glVertex3f(x2, y1, 0.0);
	glEnd();
	
    if( tex_id >= 0)
		glDisable(GL_TEXTURE_2D);
}

/**
	Draws a Striped rectangle with n strips in the XY plane.
	Normal is positive in the Z axis.
*/
void drawStripRect(float v1x, float v2x, float v1y, float v2y, int nstrips) {
	double initY = v1y, dx = (v2x - v1x) / (double) nstrips;
	double dy = (v2y - v1y) / (double) nstrips;

	for(int j = 0; j < nstrips; ++j, v1y = initY, v1x += dx) {
		glBegin(GL_QUAD_STRIP);
			glColor3f(1.0f, 1.0f, 1.0f);
			for(int i = 0; i <= nstrips; ++i, v1y += dy) {
				glNormal3i(0, 0, 1);
				glVertex3d(v1x, v1y, 0.0);
				glVertex3d(v1x + dx, v1y, 0.0);
			}
		glEnd();
	}
}

/**
	Draws a Striped rectangle with n strips in the XY plane.
	Normal is positive in the Z axis.
*/
void drawStripRect(float v1x, float v2x, float v1y, float v2y, float v1z, float v2z, int nstrips) {
	double initY = v1y, dx = (v2x - v1x) / (double) nstrips;
	double dy = (v2y - v1y) / (double) nstrips;
	double dz = (v2z - v1z) / (double) nstrips;

	for(int j = 0; j < nstrips; ++j, v1y = initY, v1x += dx, v1z += dz) {
		glBegin(GL_QUAD_STRIP);
			glColor3f(1.0f, 1.0f, 1.0f);
			for(int i = 0; i <= nstrips; ++i, v1y += dy) {
				glVertex3d(v1x, v1y, v1z);
				glVertex3d(v1x + dx, v1y, v1z + dz);
			}
		glEnd();
	}
}

/**
	Draws a unitary cube centered on the origin.
	Drawing order of planes: -ZY => ZY => -XY => XY => -XZ => XZ
	The tex array should contain the tex ids of each plane in the drawing order.
*/
void draw_cube(int tex[], int floor) {
	
	// -XZ Plane, FLOOR
	if (!floor) {
		glPushMatrix();
			glTranslated(0.0, COORDY, 0.0);
			glRotated(90, 0.0, 1.0, 0.0);
			glRotated(-90, 1.0, 0.0, 0.0);
			drawRect(-COORDX, COORDX, -COORDZ, COORDZ, (tex ? tex[0] : 0));
		glPopMatrix();
	}
	// XZ Plane
	glPushMatrix();
		glTranslated(0.0, -COORDY, 0.0);
		glRotated(90, 0.0, 1.0, 0.0);
		glRotated(90, 1.0, 0.0, 0.0);
		drawRect(-COORDX, COORDX, -COORDZ, COORDZ, (tex ? tex[1] : 0));
	glPopMatrix();

	// -ZY Plane
	glPushMatrix();
		glTranslated(-COORDX, 0.0, 0.0);
		glRotated(-90, 0.0, 1.0, 0.0);
		drawRect(-COORDZ, COORDZ, -COORDY, COORDY, (tex ? tex[2] : 0));
	glPopMatrix();
	// ZY Plane
	glPushMatrix();
		glTranslated(COORDX, 0.0, 0.0);
		glRotated(90, 0.0, 1.0, 0.0);
		drawRect(-COORDZ, COORDZ, -COORDY, COORDY, (tex ? tex[3] : 0));
	glPopMatrix();

	// -XY Plane
	glPushMatrix();
		glTranslated(0.0, 0.0, -COORDZ);
		glRotated(180, 0.0, 1.0, 0.0);
		drawRect(-COORDX, COORDX, -COORDY, COORDY, (tex ? tex[4] : 0));
	glPopMatrix();
	// XY Plane
	glPushMatrix();
		glTranslated(0.0, 0.0, COORDZ);
		drawRect(-COORDX, COORDX, -COORDY, COORDY, (tex ? tex[5] : 0));
	glPopMatrix();
}

void draw_skybox(int tex[]) {
	glPushMatrix();
		glScalef(SKYBOX_SCALE, SKYBOX_SCALE, SKYBOX_SCALE);
		// -XZ Plane
		glPushMatrix();
			glTranslated(0.0, -COORDY+0.005, 0.0);
			glRotated(90, 0.0, 1.0, 0.0);
			glRotated(-90, 1.0, 0.0, 0.0);
			drawRect(-COORDX, COORDX, -COORDZ, COORDZ, (tex ? tex[0] : 0));
		glPopMatrix();
		// XZ Plane
		glPushMatrix();
			glTranslated(0.0, COORDY-0.005, 0.0);
			glRotated(90, 0.0, 1.0, 0.0);
			glRotated(90, 1.0, 0.0, 0.0);
			drawRect(-COORDX, COORDX, -COORDZ, COORDZ, (tex ? tex[1] : 0));
		glPopMatrix();
	
		// -ZY Plane
		glPushMatrix();
			glTranslated(-COORDX+0.005, 0.0, 0.0);
			glRotated(90, 0.0, 1.0, 0.0);
			drawRect(-COORDZ, COORDZ, -COORDY, COORDY, (tex ? tex[2] : 0));
		glPopMatrix();
		// ZY Plane
		glPushMatrix();
			glTranslated(COORDX-0.005, 0.0, 0.0);
			glRotated(-90, 0.0, 1.0, 0.0);
			drawRect(-COORDZ, COORDZ, -COORDY, COORDY, (tex ? tex[3] : 0));
		glPopMatrix();
	
		// -XY Plane
		glPushMatrix();
			glTranslated(0.0, 0.0, COORDZ-0.005);
			glRotated(180, 0.0, 1.0, 0.0);
			drawRect(-COORDX, COORDX, -COORDY, COORDY, (tex ? tex[4] : 0));
		glPopMatrix();
		// XY Plane
		glPushMatrix();
			glTranslated(0.0, 0.0, -COORDZ+0.005);
			drawRect(-COORDX, COORDX, -COORDY, COORDY, (tex ? tex[5] : 0));
		glPopMatrix();
	glPopMatrix();
}


void loadControlPoints(int board) {
	
	ctrlpoints[0][1] = 0;
	ctrlpoints[1][1] = 0;
	ctrlpoints[2][1] = 0;
	ctrlpoints[3][1] = 0;

	if (board == MAIN_BOARD) {
		ctrlpoints[0][0] = BOARD_DIM_X/2;
		ctrlpoints[1][0] = -BOARD_DIM_X/2;
		ctrlpoints[2][0] = BOARD_DIM_X/2;
		ctrlpoints[3][0] = -BOARD_DIM_X/2;

		ctrlpoints[0][2] = BOARD_DIM_Z/2;
		ctrlpoints[1][2] = BOARD_DIM_Z/2;
		ctrlpoints[2][2] = -BOARD_DIM_Z/2;
		ctrlpoints[3][2] = -BOARD_DIM_Z/2;
	}
	else if(board == BENCH_BOARD1 || board == BENCH_BOARD2) {
		ctrlpoints[0][0] = BENCH_BOARD_DIM_X/2;
		ctrlpoints[1][0] = -BENCH_BOARD_DIM_X/2;
		ctrlpoints[2][0] = BENCH_BOARD_DIM_X/2;
		ctrlpoints[3][0] = -BENCH_BOARD_DIM_X/2;

		ctrlpoints[0][2] = BENCH_BOARD_DIM_Z/2;
		ctrlpoints[1][2] = BENCH_BOARD_DIM_Z/2;
		ctrlpoints[2][2] = -BENCH_BOARD_DIM_Z/2;
		ctrlpoints[3][2] = -BENCH_BOARD_DIM_Z/2;
	}
}


void draw_board_surface(int tex, int board) {

	loadControlPoints(board);
	
	glEnable(GL_TEXTURE_2D);

		glBindTexture(GL_TEXTURE_2D, tex);
			
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,   mat_floor_ambient);
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,   mat_floor_diffuse);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  mat_floor_specular);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_floor_shininess);
                
		glMap2f(GL_MAP2_VERTEX_3, 0.0, 1.0, 3, 2, 0.0, 1.0, 6, 2, &ctrlpoints[0][0]);
		glMap2f(GL_MAP2_NORMAL, 0.0, 1.0, 3, 2, 0.0, 1.0, 6, 2, &nrmlcompon[0][0]);
		glMap2f(GL_MAP2_TEXTURE_COORD_2,  0.0, 1.0, 2, 2,  0.0, 1.0, 4, 2,  &textpoints[0][0]);

		glEnable(GL_MAP2_VERTEX_3);
			glEnable(GL_MAP2_NORMAL);
				glEnable(GL_MAP2_TEXTURE_COORD_2);
					glMapGrid2f(FLOOR_SLICES, 0.0, 1.0, FLOOR_SLICES/2, 0.0, 1.0);
					glEvalMesh2(GL_FILL, 0, FLOOR_SLICES, 0, FLOOR_SLICES/2);
				glDisable(GL_MAP2_TEXTURE_COORD_2);
			glDisable(GL_MAP2_NORMAL);
		glDisable(GL_MAP2_VERTEX_3);

	glDisable(GL_TEXTURE_2D);
}

void draw_menu_message(float x, float y, char *msg) {

	int length = strlen(msg);

	// Sets Projection and Modelview matrices for 2D Rendering
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();


		glLoadIdentity();
		
		gluOrtho2D(0.0, 100.0, 0.0, 100.0);

		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
			glLoadIdentity();
			
			
			glDisable(GL_LIGHTING);
			glColor3f(1.0, 1.0, 1.0);

			
			glRasterPos2f(x, y);
			
			for(int i = 0; i < length; ++i) {
				glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, msg[i]);
			}

					
			glEnable(GL_LIGHTING);
			glMatrixMode(GL_MODELVIEW); // Resets the Matrices for 3D Rendering
		glPopMatrix();
		glMatrixMode(GL_PROJECTION);
	glPopMatrix();
}



